package operator

import (
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/config"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/dao"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/entity"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/global"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/crypto"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/model"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/util"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"net/http"
	"strconv"
	"strings"
	"time"
)

//********************************************
//Func  ：查询操作员列表，分页查询。
//Method：GET
//Usage ：
//********************************************
func QueryAllOperators(c *gin.Context) {

	pageSize, err1 := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
	if err1 != nil {
		global.Log.Error("QueryAllOperators input param pagesize error:", zap.Any("error", err1))
		c.JSON(http.StatusOK, model.Fail("请求参数错误"))
		return
	}

	page, err2 := strconv.Atoi(c.DefaultQuery("page", "1"))
	if err2 != nil {
		global.Log.Error("QueryAllOperators input param page error:", zap.Any("error", err2))
		c.JSON(http.StatusOK, model.Fail("请求参数错误"))
		return
	}

	deptId, err := strconv.ParseInt(c.DefaultQuery("deptId", ""), 10, 64)
	if err != nil {
		global.Log.Error("QueryAllOperators input param deptId error:", zap.Any("error", err2))
		c.JSON(http.StatusOK, model.Fail("请求参数错误"))
		return
	}

	mobile := c.DefaultQuery("operatorMobile", "") //邮件
	if mobile == "" {
		global.Log.Error("QueryAllOperators querier.")
		c.JSON(http.StatusOK, model.Fail("请求参数未指明查询者"))
		return
	}

	total, operators, err := dao.QueryOperatorList(page, pageSize, mobile, deptId)
	if err != nil {
		global.Log.Error("dao QueryOperatorList failed, Error:", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("未找到友情链接信息"))
		return
	}

	type OperatorList struct { //列表查询输出
		Total int64
		List  []*entity.Operator
	}
	output := &OperatorList{
		Total: total,
		List:  operators,
	}

	global.Log.Info("QueryAllOperators success, output:", zap.Any("operator list:", output))
	c.JSON(http.StatusOK, model.SuccessObject("操作成功", output))
}

//********************************************
//Func  ：查询指定操作员详情
//Method：GET
//Param：operatorId操作员ID
//Usage ：
//********************************************
func QueryOperatorDetail(c *gin.Context) {
	operatorId, err := strconv.ParseInt(c.DefaultPostForm("operatorId", ""), 10, 64)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("未指定操作员信息"))
		return
	}

	mobile := c.DefaultQuery("mobile", "")
	if operatorId <= 0 || mobile == "" {
		global.Log.Error("QueryOperatorDetail query operator detail failed, Error")
		c.JSON(http.StatusOK, model.Fail("未指定操作员信息"))
		return
	}

	operator, err := dao.QueryOperatorDetail(mobile, operatorId)
	if err != nil {
		global.Log.Error("QueryOperatorDetail query declaration failed, Error:", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("未找到指定操作员信息"))
		return
	}
	operator.Password = ""
	global.Log.Info("QueryOperatorDetail operator info:", zap.Any("operator:", operator))
	c.JSON(http.StatusOK, model.SuccessObject("操作成功", operator))
}

//********************************************
//Func  ：添加新操作员（使用手机号作为唯一ID）
//Method：POST
//Usage ：
//********************************************
func AddNewOperator(c *gin.Context) {
	var err error
	var operator entity.Operator

	operator.Name = strings.TrimSpace(c.DefaultPostForm("name", ""))
	if len(operator.Name) <= 0 {
		c.JSON(http.StatusOK, model.Fail("姓名不能为空"))
		return
	}
	operator.Nickname = strings.TrimSpace(c.DefaultPostForm("nickname", ""))
	operator.Password = c.DefaultPostForm("password", "")
	operator.Mobile = strings.TrimSpace(c.DefaultPostForm("mobile", ""))
	if len(operator.Mobile) <= 0 {
		c.JSON(http.StatusOK, model.Fail("手机号码不能为空"))
		return
	}
	operator.CreatedAt = time.Now()
	operator.Telephone = c.DefaultPostForm("telephone", "")
	deptIdStr := c.DefaultPostForm("deptId", "")
	operator.DeptId, err = strconv.Atoi(deptIdStr)
	if err != nil {
		global.Log.Error("AddNewOperator on find dept id")
		c.JSON(http.StatusOK, model.Fail("未指定部门信息"))
		return
	}
	operator.DeptName = strings.TrimSpace(c.DefaultPostForm("deptName", "")) //部门
	operator.Status = 0
	operator.IsAdmin, err = strconv.ParseBool(c.DefaultPostForm("isAdmin", "false"))
	global.Log.Info("AddNewOperator: ", zap.Any("operator:", operator))

	newoperator, err := dao.InsertOperator(operator)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("未指定部门信息"))
		global.Log.Error("dao.InsertOperator failed:", zap.Any("error", err))
		return
	}

	global.Log.Info("AddNewOperator success, new link:", zap.Any("new operator", newoperator))
	//c.JSON(http.StatusOK, model.SuccessObject("操作成功", newoperator))
	c.JSON(http.StatusOK, model.SuccessObject("操作成功", newoperator.Id))
}

//********************************************
//Func  ：更新Operator信息
//Method：POST
//Usage ：
//********************************************
func UpdateOperator(c *gin.Context) {
	var err error
	var operator entity.Operator

	operator.Name = strings.TrimSpace(c.DefaultPostForm("name", ""))
	operator.Nickname = strings.TrimSpace(c.DefaultPostForm("nickname", ""))
	operator.Mobile = strings.TrimSpace(c.DefaultPostForm("mobile", ""))
	if len(operator.Mobile) <= 0 {
		c.JSON(http.StatusOK, model.Fail("手机号码不能为空"))
		return
	}
	operator.Telephone = c.DefaultPostForm("telephone", "")
	deptIdStr := strings.TrimSpace(c.DefaultPostForm("deptId", ""))
	operator.DeptId, err = strconv.Atoi(deptIdStr)
	if err != nil {
		global.Log.Error("AddNewOperator on find dept id")
		c.JSON(http.StatusOK, model.Fail("未指定部门信息"))
		return
	} else {
		operator.DeptName = c.DefaultPostForm("deptName", "")
	}

	operator.DeptName = strings.TrimSpace(c.DefaultPostForm("deptName", ""))
	global.Log.Info("UpdateOperator: ", zap.Any("operator:", operator))

	err = dao.UpdateOperator(operator)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("操作失败"))
		global.Log.Error("UpdateOperator failed .", zap.Any("error", err))
		return
	}

	c.JSON(http.StatusOK, model.Success("操作成功"))
	//	c.JSON(http.StatusOK, model.SuccessObject("操作成功", operator))
	global.Log.Info("UpdateOperator success, new link:", zap.Any("update operator", operator))
}

//********************************************
//Func  ：Operator删除
//Method：DELETE
//Usage ：操作员-账号注销
//********************************************
func DeleteOperator(c *gin.Context) {

	operatorId, err := strconv.ParseInt(c.DefaultPostForm("operatorId", ""), 10, 64)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("未指定操作员信息"))
		return
	}

	mobile := strings.TrimSpace(c.DefaultQuery("mobile", ""))
	if operatorId <= 0 || mobile == "" {
		global.Log.Error("DeleteOperator query operator detail failed, Error")
		c.JSON(http.StatusOK, model.Fail("未指定操作员信息"))
		return
	}

	err = dao.DeleteOperator(mobile, operatorId)
	if err != nil {
		global.Log.Error("DeleteOperator query declaration failed, Error:", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("删除失败"))
		return
	}
	global.Log.Info("DeleteOperator :", zap.Any("operator:", mobile))
	c.JSON(http.StatusOK, model.Success("删除成功"))
}

//********************************************
//Func  ：操作员修改密码（验证原密码后，刷新新密码）
//Method：POST
//输入：手机号、旧密码、新密码
//Usage ：
//********************************************
func OperatorChangePassword(c *gin.Context) {
	operator := c.MustGet("USER").(*entity.Operator)
	req := entity.ReqChangePasswordOperator{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("OperatorChangePassword ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密密码密文
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("OperatorChangePassword invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	oldByte, err := cryptoAsymmetric.DecryptWithBase64(req.OldPassword, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("OperatorChangePassword sm2 decrypt oldpassword fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("旧密码无效"))
		return
	}
	newByte, err := cryptoAsymmetric.DecryptWithBase64(req.NewPassword, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("OperatorChangePassword sm2 decrypt newpassword fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("新密码无效"))
		return
	}
	// 校验旧密码是否正确
	cryptoDigest, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if !ok {
		global.Log.Error("OperatorChangePassword invalid crypto", zap.Any("type", crypto.CryptoSM3))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	oldpwdSum, err := cryptoDigest.Sum(oldByte)
	if err != nil {
		global.Log.Error("OperatorChangePassword sm3 sum fail", zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	if oldpwdSum != operator.Password {
		global.Log.Error("OperatorChangePassword invalid oldPassword")
		c.JSON(http.StatusUnauthorized, model.Fail("无效旧密码"))
		return
	}
	// 校验新密码强度
	if !util.IsStrongPassword(string(newByte)) {
		c.JSON(http.StatusBadRequest, model.Fail("新密码强度不足"))
		return
	}
	// 新密码摘要
	pwdSum, err := cryptoDigest.Sum(newByte)
	if err != nil {
		global.Log.Error("OperatorChangePassword sm3 sum fail", zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	// 更新密码
	err = dao.OperatorChangePassword("", operator.Id, pwdSum)
	if err != nil {
		c.JSON(http.StatusInternalServerError, model.Fail("操作失败"))
		return
	} else {
		c.JSON(http.StatusOK, model.Success("密码更新成功"))
		return
	}
}

//********************************************
//Func  ：操作员重置密码（经过短信验证码后，刷新新密码）
//Method：POST
//输入：手机号、验证码、新密码
//Usage ：
//********************************************
func OperatorResetPassword(c *gin.Context) {
	operator := c.MustGet("USER").(*entity.Operator)
	req := entity.ReqRetrievePasswordMsgVerCodeCustomer{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("OperatorResetPassword ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密密码密文
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("OperatorResetPassword invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordByte, err := cryptoAsymmetric.DecryptWithBase64(req.Password, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("OperatorResetPassword sm2 decrypt password fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("密码无效"))
		return
	}
	mobileByte, err := cryptoAsymmetric.DecryptWithBase64(req.Mobile, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("OperatorResetPassword sm2 decrypt mobile fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("手机号无效"))
		return
	}
	req.Mobile = string(mobileByte)
	// 校验手机号（如果已绑定）
	if len(operator.Mobile) > 0 && req.Mobile != operator.Mobile {
		global.Log.Error("OperatorResetPassword invalid mobile fail", zap.Any("db", operator.Mobile), zap.Any("req", req.Mobile))
		c.JSON(http.StatusBadRequest, model.Fail("手机号无效"))
		return
	}
	// 校验短信验证码
	if err := global.MsgCodeGenerator.Check(req.Mobile, req.Code); err != nil {
		global.Log.Error("OperatorResetPassword MsgCodeGenerator.Check fail,", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail(err.Error()))
		return
	}

	// 校验密码强度
	if !util.IsStrongPassword(string(passwordByte)) {
		c.JSON(http.StatusOK, model.Fail("密码强度不足"))
		return
	}
	// 密码摘要
	cryptoDigest, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if !ok {
		global.Log.Error("OperatorResetPassword invalid crypto", zap.Any("type", crypto.CryptoSM3))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordSum, err := cryptoDigest.Sum(passwordByte)
	if err != nil {
		global.Log.Error("OperatorResetPassword sm3 sum fail", zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	// 更细密码
	err = dao.OperatorChangePassword("", operator.Id, string(passwordSum))
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("操作失败"))
		return
	} else {
		c.JSON(http.StatusOK, model.Success("密码重置成功"))
		return
	}
}

//********************************************
//Func  ：操作员短信码登录
//Method：POST
//Usage ：
//********************************************
func LoginSmsOperator(c *gin.Context) {
	req := entity.ReqSmsLoginOperator{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("LoginSmsOperator ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密手机号码
	cryptoInst, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("LoginSmsOperator invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	mobileByte, err := cryptoInst.DecryptWithBase64(req.Mobile, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("LoginSmsOperator invalid sm2.Decrypt", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	req.Mobile = string(mobileByte)
	operator, err := dao.QueryOperatorDetail(req.Mobile, 0)
	if err != nil {
		global.Log.Error("LoginSmsOperator.QueryOperatorDetail fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("未找到该用户信息"))
		return
	}
	// 校验验证码
	if err = global.MsgCodeGenerator.Check(req.Mobile, req.Code); err != nil {
		global.Log.Error("LoginSmsOperator MsgCodeGenerator.Check fail,", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail(err.Error()))
		return
	}
	token, err := util.GetJwtToken(operator.Id, config.Token().Expire, config.Token().Key)
	if err != nil {
		global.Log.Error("LoginSmsOperator GetJwtToken failed.", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	log := entity.OperatorLoginLog{
		Id:         util.GenSnowflakeInt64(),
		OperatorId: operator.Id,
		LoginTime:  time.Now(),
		LoginType:  entity.LoginType_MobileVercode,
		Token:      token,
	}
	err = dao.InsertOperatorLoginLog(log)
	if err != nil {
		global.Log.Error("LoginSmsOperator.InsertOperatorLoginLog failed.", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("登陆失败"))
		return
	}
	rsp := entity.RspLoginOperator{
		Token:  token,
		Expire: config.Token().Expire,
	}
	c.JSON(http.StatusOK, model.SuccessObject("登录成功", rsp))
	global.Log.Info("LoginOperator success.", zap.Any("token", rsp))
}

//********************************************
//Func  ：操作员登录
//Method：POST
//Usage ：
//********************************************
func LoginOperator(c *gin.Context) {
	req := entity.ReqPwdLoginOperator{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("LoginOperator ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密手机号码
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("LoginOperator invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	mobileByte, err := cryptoAsymmetric.DecryptWithBase64(req.Mobile, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("LoginOperator invalid sm2.Decrypt", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	req.Mobile = string(mobileByte)
	// 解密密码
	passwordByte, err := cryptoAsymmetric.DecryptWithBase64(req.Password, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("LoginOperator invalid sm2.Decrypt", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	// 获取密码摘要
	cryptoDigest, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if !ok {
		global.Log.Error("LoginOperator invalid crypto", zap.Any("type", crypto.CryptoSM3))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordDigest, err := cryptoDigest.Sum(passwordByte)
	if err != nil {
		global.Log.Error("LoginOperator invalid sm3.Sum", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	req.Password = string(passwordDigest)
	// 校验密码
	operator, err := dao.QueryOperatorDetail(req.Mobile, 0)
	if err != nil {
		global.Log.Error("LoginOperator.QueryOperatorDetail fail", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("未找到该用户信息"))
		return
	}
	if req.Password != operator.Password {
		global.Log.Error("LoginOperator invalid password")
		global.Log.Debug("LoginOperator invalid password", zap.Any("password", req.Password))
		c.JSON(http.StatusOK, model.Fail("密码错误"))
		return
	}
	// 生成token
	token, err := util.GetJwtToken(operator.Id, config.Token().Expire, config.Token().Key)
	if err != nil {
		global.Log.Error("GetJwtToken failed.", zap.Any("error", err))
	}
	// 记录loginlog
	log := entity.OperatorLoginLog{
		Id:         util.GenSnowflakeInt64(),
		OperatorId: operator.Id,
		LoginTime:  time.Now(),
		LoginType:  entity.LoginType_MobilePwd,
		Token:      token,
	}
	err = dao.InsertOperatorLoginLog(log)
	if err != nil {
		global.Log.Error("InsertOperatorLoginLog failed.", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("登陆失败"))
		return
	}
	rsp := entity.RspLoginOperator{
		Token:  token,
		Expire: config.Token().Expire,
	}
	c.JSON(http.StatusOK, model.SuccessObject("登录成功", rsp))
	global.Log.Info("LoginOperator success.", zap.Any("new token", rsp))
}

//********************************************
//Func  ：操作员登出
//Method：POST
//Usage ：
//********************************************
func LogoutOperator(c *gin.Context) {
	//todo ，del token吗
	var log entity.OperatorLoginLog
	mobile := strings.TrimSpace(c.DefaultPostForm("mobile", ""))
	if mobile == "" {
		global.Log.Error("LogoutOperator mobile is none.")
		c.JSON(http.StatusOK, model.Fail("用户信息错误"))
		return
	}

	log.OperatorId, _ = strconv.ParseInt(c.DefaultPostForm("operatorId", ""), 10, 64)
	log.LogoutTime = time.Now()
	log.LoginOrLogout = 1
	dao.InsertOperatorLoginLog(log)
}
